package com.binance.autojob.utils;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.binance.autojob.annotation.MyScheduledJob;
import com.binance.autojob.listener.EnvironmentPreparedListner;
import com.xxl.job.core.biz.AdminBiz;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.glue.GlueTypeEnum;
import com.xxl.job.core.handler.IJobHandler;
import com.xxl.job.core.model.XxlJobInfo;
import com.xxl.job.core.rpc.netcom.NetComClientProxy;

public final class JobTimer {
    private static Logger logger = LoggerFactory.getLogger(JobTimer.class);

    private Map<String, MyScheduledJob> jobStore;


    private ConcurrentLinkedQueue<IJobHandler> fail = new ConcurrentLinkedQueue<IJobHandler>();

    public JobTimer(Map<String, MyScheduledJob> jobStore) {
        this.jobStore = jobStore;
    }


    private final ScheduledExecutorService POOL = Executors.newScheduledThreadPool(2, new ThreadFactory() {

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    });

    public void start() {
        // 失败处理
        POOL.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    while (!CollectionUtils.isEmpty(fail)) {
                        IJobHandler handler = fail.peek();

                        ReturnT<String> ret = handler.execute("");
                        boolean ok = success(ret);
                        if (ok) {
                            logger.info(">>>>>>>>auto-regist->RESULT:{}", ret);
                            fail.poll();
                        }
                    }
                } catch (Exception e) {
                    logger.warn(">>>>>>>>auto-regist-> retry failed", e);
                }
            }

            private boolean success(ReturnT<String> ret) {
                return ret != null && ret.getCode() == ReturnT.SUCCESS_CODE;
            }
        });

        // 自动注册
        POOL.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {

                logger.info(">>>>>>>>auto-regist,jobStore has jobs:{},", jobStore.size());
                if (!CollectionUtils.isEmpty(jobStore)) {
                    try {
                        for (Entry<String, MyScheduledJob> jobEntity : jobStore.entrySet()) {
                            String name = jobEntity.getKey();
                            MyScheduledJob scheduled = jobEntity.getValue();

                            logger.info(">>>>>>>>auto-regist->name:{},", name);

                            boolean needAdd = true;

                            // executorName
                            String executorName = getByDefault(scheduled.executorName(), getExeNameFromConfig());
                            needAdd = needAdd(executorName, name + "==>lost executorName");

                            // cron
                            String cron = getByDefault(scheduled.cron(), "");
                            needAdd = needAdd(cron, name + "==>lost cron");

                            // author
                            String author = getByDefault(scheduled.author(),
                                    EnvironmentPreparedListner.ENV.getProperty(AutoJobContants.XXL_JOB_TASK_AUTHOR));
                            needAdd = needAdd(author, name + "==>lost author");

                            // alertEmail
                            String alarmEmail = getByDefault(scheduled.alarmEmail(),
                                    EnvironmentPreparedListner.ENV.getProperty(AutoJobContants.XXL_JOB_TASK_EMAIL));
                            needAdd = needAdd(alarmEmail, name + "==>lost alarmEmail");

                            // 上述情况需要手动注册
                            if (needAdd == false) {
                                jobStore.remove(name);
                                return;
                            }
                            // 样板代码
                            XxlJobInfo job = composeJob(name, scheduled, executorName, cron, author, alarmEmail);
                            // 遍历所有的胡策中心代理客户端
                            int adminNum = 0;
                            boolean success = true;
                            for (final AdminBiz adminBiz : getAdmins()) {
                                ReturnT<String> ret = adminBiz.addJob(job);// 已经存在的job，返回success
                                boolean ok = ret != null && ret.getCode() == ReturnT.SUCCESS_CODE;
                                if (ok) {
                                    logger.info(">>>>>>>>auto-regist->RESULT:{},job-info:{},admin-number:{}", ret, job,
                                            adminNum++);
                                } else {
                                    logger.warn(">>>>>>>>auto-regist->RESULT:{},job-info:{},admin-number:{}", ret, job,
                                            adminNum++);
                                    // 失败重试队列
                                    fail.add(new IJobHandler() {

                                        @Override
                                        public ReturnT<String> execute(String param) throws Exception {
                                            return adminBiz.addJob(job);
                                        }
                                    });
                                }
                                success = success && ok;
                            }
                            if (success) {
                                // 成功注册全部调度中心，删除此job
                                jobStore.remove(name);
                            }
                            if (CollectionUtils.isEmpty(jobStore)) {
                                // 全部注册完回收资源
                                POOL.shutdown();
                                logger.info("自动注册调度中心完毕，done！");
                            }
                        }
                    } catch (Exception e) {
                        logger.error("自动注册调度中心异常。", e);
                    }
                }
            }

        }, 5, 15, TimeUnit.SECONDS);
    }


    private XxlJobInfo composeJob(String name, MyScheduledJob scheduled, String executorName, String cron,
            String author, String alarmEmail) {
        XxlJobInfo job = new XxlJobInfo();
        job.setAppName(executorName);// 按照名称查找所在调度中心的ID，重复名称的也注册
        job.setJobCron(cron);
        job.setAlarmEmail(alarmEmail);
        job.setAuthor(author);
        job.setExecutorHandler(name);// 执行器按照名称找要执行的方法

        job.setJobDesc(getByDefault(scheduled.jobDesc(), name));// title默认就是名字
        job.setExecutorRouteStrategy(getByDefault(scheduled.routeStrategy(), "FIRST"));
        job.setExecutorParam(getByDefault(scheduled.param(), ""));
        job.setExecutorBlockStrategy(
                getByDefault(scheduled.blockStrategy().name(), ExecutorBlockStrategyEnum.SERIAL_EXECUTION.name()));
        job.setExecutorFailStrategy(getByDefault(scheduled.failStrategy(), "FAIL_RETRY"));
        job.setGlueType(getByDefault(scheduled.glueType().name(), GlueTypeEnum.BEAN.name()));
        job.setGlueSource(getByDefault(scheduled.glueSource(), ""));// 若果是脚本任务，此处就是执行的脚本
        job.setGlueRemark("GLUE代码初始化");
        job.setChildJobId(getByDefault(scheduled.childJobId(), ""));
        Date now = new Date();
        job.setAddTime(now);
        job.setUpdateTime(now);
        return job;
    }

    private boolean needAdd(String input, String msg) {
        if (StringUtils.isEmpty(input)) {
            logger.warn(">>>>>>>>auto-regist->{},you need regist it by youself!", msg);
            return false;
        }
        return true;
    }

    private static List<AdminBiz> getAdmins() throws Exception {
        List<AdminBiz> lst = XxlJobExecutor.getAdminBizList();
        if (CollectionUtils.isEmpty(lst)) {
            lst = new LinkedList<AdminBiz>();
            String adminAddresses = EnvironmentPreparedListner.ENV.getProperty(AutoJobContants.XXL_JOB_ADMIN_ADDRESSES);
            String accessToken = EnvironmentPreparedListner.ENV.getProperty(AutoJobContants.XXL_JOB_ACCESS_TOKEN);
            if (adminAddresses != null && adminAddresses.trim().length() > 0) {
                for (String address : adminAddresses.trim().split(",")) {
                    if (address != null && address.trim().length() > 0) {
                        String addressUrl = address.concat(AdminBiz.MAPPING);
                        AdminBiz adminBiz =
                                (AdminBiz) new NetComClientProxy(AdminBiz.class, addressUrl, accessToken).getObject();
                        lst.add(adminBiz);
                    }
                }
            }
        }
        return lst;
    }

    /**
     * 1先查注释 2在查配置 3按照
     * 
     * @return
     */
    private static String getExeNameFromConfig() {
        String name = "";
        try {
            name = EnvironmentPreparedListner.ENV.getProperty(AutoJobContants.XXL_JOB_EXECUTOR_APPNAME);
        } finally {
        }
        return name;
    }

    private static String getByDefault(final String value, final String defaultVal) {
        if (StringUtils.isEmpty(value)) {
            return defaultVal;
        }
        // 支持占位符
        return EnvironmentPreparedListner.ENV.resolvePlaceholders(value);
    }
}

